<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>创建推理服务</title>
<script src="https://cdn.tailwindcss.com/3.4.16"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.6.0/remixicon.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#3B82F6',
secondary: '#6B7280'
},
borderRadius: {
'none': '0px',
'sm': '4px',
DEFAULT: '8px',
'md': '12px',
'lg': '16px',
'xl': '20px',
'2xl': '24px',
'3xl': '32px',
'full': '9999px',
'button': '8px'
}
}
}
}
</script>
<style>
:where([class^="ri-"])::before { content: "\f3c2"; }
.custom-scrollbar::-webkit-scrollbar { width: 4px; }
.custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
.custom-scrollbar::-webkit-scrollbar-thumb { background: #E5E7EB; border-radius: 4px; }
.custom-checkbox { appearance: none; }
.custom-radio { appearance: none; }
.custom-range { appearance: none; }
.custom-range::-webkit-slider-track { height: 4px; background: #E5E7EB; border-radius: 2px; }
.custom-range::-webkit-slider-thumb { appearance: none; width: 16px; height: 16px; background: #3B82F6; border-radius: 50%; cursor: pointer; }
.custom-range::-moz-range-track { height: 4px; background: #E5E7EB; border-radius: 2px; border: none; }
.custom-range::-moz-range-thumb { width: 16px; height: 16px; background: #3B82F6; border-radius: 50%; cursor: pointer; border: none; }
.custom-switch { width: 44px; height: 24px; background: #E5E7EB; border-radius: 12px; position: relative; cursor: pointer; transition: all 0.3s; }
.custom-switch.active { background: #3B82F6; }
.custom-switch::after { content: ''; position: absolute; width: 20px; height: 20px; background: white; border-radius: 50%; top: 2px; left: 2px; transition: all 0.3s; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.custom-switch.active::after { left: 22px; }
</style>
</head>
<body class="bg-gray-50">
<div class="min-h-screen">
<header class="bg-white border-b border-gray-200 px-6 py-4">
<div class="flex items-center justify-between">
<div class="flex items-center gap-4">
<a href="https://readdy.ai/home/42b69b4b-e314-4f8d-9e2f-98a4952face1/83af26fe-6a2c-4b4d-9dc6-9dd0ca282c18" data-readdy="true" class="flex items-center gap-2 text-gray-600 hover:text-gray-900 transition-colors">
<div class="w-8 h-8 flex items-center justify-center">
<i class="ri-arrow-left-line text-lg"></i>
</div>
<span class="text-sm">返回</span>
</a>
<div class="w-px h-6 bg-gray-200"></div>
<h1 class="text-xl font-medium text-gray-900">创建推理服务</h1>
</div>
<div class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<i class="ri-user-line text-gray-600"></i>
</div>
</div>
</header>

<div class="max-w-4xl mx-auto p-6">
<form id="createServiceForm" class="space-y-8">
<div class="bg-white rounded-xl p-6 shadow-sm">
<h2 class="text-lg font-medium text-gray-900 mb-6">基本信息</h2>
<div class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">服务名称 <span class="text-red-500">*</span></label>
<input type="text" id="serviceName" required class="w-full px-4 py-3 border border-gray-200 rounded-button focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm" placeholder="请输入服务名称">
<div class="text-xs text-red-500 mt-1 hidden" id="serviceNameError">服务名称不能为空</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-3">服务类型 <span class="text-red-500">*</span></label>
<div class="flex gap-4">
<label class="flex items-center cursor-pointer">
<div class="relative">
<input type="radio" name="serviceType" value="realtime" class="custom-radio sr-only" required>
<div class="w-5 h-5 border-2 border-gray-300 rounded-full flex items-center justify-center">
<div class="w-2.5 h-2.5 bg-primary rounded-full hidden"></div>
</div>
</div>
<span class="ml-3 text-sm text-gray-700">实时推理</span>
</label>
<label class="flex items-center cursor-pointer">
<div class="relative">
<input type="radio" name="serviceType" value="batch" class="custom-radio sr-only">
<div class="w-5 h-5 border-2 border-gray-300 rounded-full flex items-center justify-center">
<div class="w-2.5 h-2.5 bg-primary rounded-full hidden"></div>
</div>
</div>
<span class="ml-3 text-sm text-gray-700">批量推理</span>
</label>
</div>
</div>
</div>
</div>

<div class="bg-white rounded-xl p-6 shadow-sm">
<h2 class="text-lg font-medium text-gray-900 mb-6">模型配置</h2>
<div class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">模型选择 <span class="text-red-500">*</span></label>
<div class="relative">
<button type="button" id="modelSelect" class="w-full px-4 py-3 border border-gray-200 rounded-button focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm text-left bg-white flex items-center justify-between">
<span class="text-gray-500">请选择模型</span>
<div class="w-4 h-4 flex items-center justify-center">
<i class="ri-arrow-down-s-line text-gray-400"></i>
</div>
</button>
<div id="modelDropdown" class="absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-button shadow-lg z-10 hidden">
<div class="py-1">
<div class="px-4 py-2 hover:bg-gray-50 cursor-pointer text-sm" data-value="bert-base">BERT Base (文本分类)</div>
<div class="px-4 py-2 hover:bg-gray-50 cursor-pointer text-sm" data-value="resnet-50">ResNet-50 (图像识别)</div>
<div class="px-4 py-2 hover:bg-gray-50 cursor-pointer text-sm" data-value="gpt-3.5">GPT-3.5 (文本生成)</div>
<div class="px-4 py-2 hover:bg-gray-50 cursor-pointer text-sm" data-value="yolo-v8">YOLO v8 (目标检测)</div>
</div>
</div>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">模型版本</label>
<div class="relative">
<button type="button" id="versionSelect" class="w-full px-4 py-3 border border-gray-200 rounded-button focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm text-left bg-white flex items-center justify-between" disabled>
<span class="text-gray-400">请先选择模型</span>
<div class="w-4 h-4 flex items-center justify-center">
<i class="ri-arrow-down-s-line text-gray-400"></i>
</div>
</button>
<div id="versionDropdown" class="absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-button shadow-lg z-10 hidden">
<div class="py-1">
<div class="px-4 py-2 hover:bg-gray-50 cursor-pointer text-sm" data-value="v1.0.0">v1.0.0 (稳定版)</div>
<div class="px-4 py-2 hover:bg-gray-50 cursor-pointer text-sm" data-value="v1.1.0">v1.1.0 (最新版)</div>
<div class="px-4 py-2 hover:bg-gray-50 cursor-pointer text-sm" data-value="v0.9.5">v0.9.5 (测试版)</div>
</div>
</div>
</div>
</div>
</div>
</div>

<div class="bg-white rounded-xl p-6 shadow-sm">
<h2 class="text-lg font-medium text-gray-900 mb-6">资源配置</h2>
<div class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-3">CPU 配置</label>
<div class="flex items-center gap-4">
<input type="range" id="cpuRange" class="custom-range flex-1" min="1" max="16" value="4" step="1">
<div class="w-20 text-sm text-gray-600">
<span id="cpuValue">4</span> 核
</div>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-3">内存配置</label>
<div class="flex items-center gap-4">
<input type="range" id="memoryRange" class="custom-range flex-1" min="2" max="64" value="8" step="2">
<div class="w-20 text-sm text-gray-600">
<span id="memoryValue">8</span> GB
</div>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-3">GPU 配置</label>
<div class="flex items-center gap-4 mb-3">
<div class="custom-switch" id="gpuSwitch"></div>
<span class="text-sm text-gray-600">启用 GPU 加速</span>
</div>
<div id="gpuConfig" class="hidden">
<div class="flex items-center gap-4">
<input type="range" id="gpuRange" class="custom-range flex-1" min="1" max="8" value="1" step="1">
<div class="w-20 text-sm text-gray-600">
<span id="gpuValue">1</span> 卡
</div>
</div>
</div>
</div>
</div>
</div>

<div class="bg-white rounded-xl p-6 shadow-sm">
<h2 class="text-lg font-medium text-gray-900 mb-6">网络配置</h2>
<div class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">服务端口</label>
<input type="number" id="servicePort" class="w-full px-4 py-3 border border-gray-200 rounded-button focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm" value="8080" min="1024" max="65535">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-3">访问控制</label>
<div class="flex gap-4">
<label class="flex items-center cursor-pointer">
<div class="relative">
<input type="radio" name="accessControl" value="public" class="custom-radio sr-only" checked>
<div class="w-5 h-5 border-2 border-gray-300 rounded-full flex items-center justify-center">
<div class="w-2.5 h-2.5 bg-primary rounded-full"></div>
</div>
</div>
<span class="ml-3 text-sm text-gray-700">公网访问</span>
</label>
<label class="flex items-center cursor-pointer">
<div class="relative">
<input type="radio" name="accessControl" value="private" class="custom-radio sr-only">
<div class="w-5 h-5 border-2 border-gray-300 rounded-full flex items-center justify-center">
<div class="w-2.5 h-2.5 bg-primary rounded-full hidden"></div>
</div>
</div>
<span class="ml-3 text-sm text-gray-700">内网访问</span>
</label>
</div>
</div>
</div>
</div>

<div class="bg-white rounded-xl p-6 shadow-sm">
<h2 class="text-lg font-medium text-gray-900 mb-6">环境变量设置</h2>
<div class="space-y-4" id="envVarsContainer">
<div class="flex gap-3 items-start env-var-row">
<input type="text" placeholder="变量名" class="flex-1 px-4 py-3 border border-gray-200 rounded-button focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
<input type="text" placeholder="变量值" class="flex-1 px-4 py-3 border border-gray-200 rounded-button focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
<button type="button" class="w-10 h-10 flex items-center justify-center text-gray-400 hover:text-red-500 transition-colors remove-env-var" disabled>
<i class="ri-delete-bin-line"></i>
</button>
</div>
</div>
<button type="button" id="addEnvVar" class="flex items-center gap-2 text-primary hover:text-blue-600 transition-colors text-sm">
<div class="w-4 h-4 flex items-center justify-center">
<i class="ri-add-line"></i>
</div>
<span>添加环境变量</span>
</button>
</div>
</form>
</div>

<div class="sticky bottom-0 bg-white border-t border-gray-200 px-6 py-4">
<div class="max-w-4xl mx-auto flex items-center justify-end gap-4">
<a href="https://readdy.ai/home/42b69b4b-e314-4f8d-9e2f-98a4952face1/83af26fe-6a2c-4b4d-9dc6-9dd0ca282c18" data-readdy="true">
<button type="button" class="px-6 py-3 border border-gray-200 text-gray-700 rounded-button hover:bg-gray-50 transition-colors whitespace-nowrap !rounded-button">取消</button>
</a>
<button type="submit" form="createServiceForm" class="px-6 py-3 bg-primary text-white rounded-button hover:bg-blue-600 transition-colors whitespace-nowrap !rounded-button">创建服务</button>
</div>
</div>
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function() {
const name = this.name;
document.querySelectorAll(`input[name="${name}"]`).forEach(r => {
const circle = r.parentNode.querySelector('div');
const dot = circle.querySelector('div');
if (r.checked) {
circle.classList.add('border-primary');
dot.classList.remove('hidden');
} else {
circle.classList.remove('border-primary');
dot.classList.add('hidden');
}
});
});
if (radio.checked) {
radio.dispatchEvent(new Event('change'));
}
});

const cpuRange = document.getElementById('cpuRange');
const cpuValue = document.getElementById('cpuValue');
cpuRange.addEventListener('input', function() {
cpuValue.textContent = this.value;
});

const memoryRange = document.getElementById('memoryRange');
const memoryValue = document.getElementById('memoryValue');
memoryRange.addEventListener('input', function() {
memoryValue.textContent = this.value;
});

const gpuRange = document.getElementById('gpuRange');
const gpuValue = document.getElementById('gpuValue');
gpuRange.addEventListener('input', function() {
gpuValue.textContent = this.value;
});

const gpuSwitch = document.getElementById('gpuSwitch');
const gpuConfig = document.getElementById('gpuConfig');
let gpuEnabled = false;
gpuSwitch.addEventListener('click', function() {
gpuEnabled = !gpuEnabled;
if (gpuEnabled) {
this.classList.add('active');
gpuConfig.classList.remove('hidden');
} else {
this.classList.remove('active');
gpuConfig.classList.add('hidden');
}
});

const modelSelect = document.getElementById('modelSelect');
const modelDropdown = document.getElementById('modelDropdown');
const versionSelect = document.getElementById('versionSelect');
const versionDropdown = document.getElementById('versionDropdown');

modelSelect.addEventListener('click', function() {
modelDropdown.classList.toggle('hidden');
});

modelDropdown.addEventListener('click', function(e) {
if (e.target.dataset.value) {
const selectedText = e.target.textContent;
modelSelect.querySelector('span').textContent = selectedText;
modelSelect.querySelector('span').classList.remove('text-gray-500');
modelSelect.querySelector('span').classList.add('text-gray-900');
modelDropdown.classList.add('hidden');
versionSelect.disabled = false;
versionSelect.querySelector('span').textContent = '请选择版本';
versionSelect.querySelector('span').classList.remove('text-gray-400');
versionSelect.querySelector('span').classList.add('text-gray-500');
}
});

versionSelect.addEventListener('click', function() {
if (!this.disabled) {
versionDropdown.classList.toggle('hidden');
}
});

versionDropdown.addEventListener('click', function(e) {
if (e.target.dataset.value) {
const selectedText = e.target.textContent;
versionSelect.querySelector('span').textContent = selectedText;
versionSelect.querySelector('span').classList.remove('text-gray-500');
versionSelect.querySelector('span').classList.add('text-gray-900');
versionDropdown.classList.add('hidden');
}
});

document.addEventListener('click', function(e) {
if (!modelSelect.contains(e.target) && !modelDropdown.contains(e.target)) {
modelDropdown.classList.add('hidden');
}
if (!versionSelect.contains(e.target) && !versionDropdown.contains(e.target)) {
versionDropdown.classList.add('hidden');
}
});

const envVarsContainer = document.getElementById('envVarsContainer');
const addEnvVarBtn = document.getElementById('addEnvVar');

function updateRemoveButtons() {
const rows = envVarsContainer.querySelectorAll('.env-var-row');
rows.forEach((row, index) => {
const removeBtn = row.querySelector('.remove-env-var');
if (rows.length === 1) {
removeBtn.disabled = true;
removeBtn.classList.add('text-gray-400');
removeBtn.classList.remove('hover:text-red-500');
} else {
removeBtn.disabled = false;
removeBtn.classList.remove('text-gray-400');
removeBtn.classList.add('hover:text-red-500');
}
});
}

addEnvVarBtn.addEventListener('click', function() {
const newRow = document.createElement('div');
newRow.className = 'flex gap-3 items-start env-var-row';
newRow.innerHTML = `
<input type="text" placeholder="变量名" class="flex-1 px-4 py-3 border border-gray-200 rounded-button focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
<input type="text" placeholder="变量值" class="flex-1 px-4 py-3 border border-gray-200 rounded-button focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm">
<button type="button" class="w-10 h-10 flex items-center justify-center text-gray-400 hover:text-red-500 transition-colors remove-env-var">
<i class="ri-delete-bin-line"></i>
</button>
`;
envVarsContainer.appendChild(newRow);
updateRemoveButtons();
});

envVarsContainer.addEventListener('click', function(e) {
if (e.target.closest('.remove-env-var') && !e.target.closest('.remove-env-var').disabled) {
e.target.closest('.env-var-row').remove();
updateRemoveButtons();
}
});

updateRemoveButtons();

const form = document.getElementById('createServiceForm');
form.addEventListener('submit', function(e) {
e.preventDefault();
const serviceName = document.getElementById('serviceName').value.trim();
const serviceNameError = document.getElementById('serviceNameError');

if (!serviceName) {
serviceNameError.classList.remove('hidden');
return;
} else {
serviceNameError.classList.add('hidden');
}

const serviceType = document.querySelector('input[name="serviceType"]:checked');
if (!serviceType) {
alert('请选择服务类型');
return;
}

alert('服务创建成功！');
});
});
</script>
</body>
</html>